﻿using Core.DataAccess.Model.iTool.Gateway;
using Core.Framework;
using Environment.Interfaces;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;

namespace Dashboard
{
    public class DrawerServiceOptions
    {
        public DrawerServiceOptions()
        {
            this.Services.Add(new SqlServer());
            //this.Services.Add(new RabbitMQ());
            //this.Services.Add(new Redis());
            this.Services.Add(new MongoDB());
            this.Services.Add(new Elasticsearch());
            this.Services.Add(new iToolService());
            this.Services.Add(new iToolFileCenter());
            this.Services.Add(new iToolClient());

        }

        public List<AbstractServer> Services { get; set; } = new List<AbstractServer>();

    }

    public class iToolService : AbstractServer
    {
        public override Task<(bool, string)> Backup(InstallOptions options)
        {
            return Task.FromResult((true, string.Empty));
        }

        public async override Task<(bool, string)> Install(Node node, InstallOptions options)
        {
            var command = $"wget -O installiToolServer.sh https://cloud.itool.store/iToolServer.sh && sudo bash installiToolServer.sh";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            options.Status = 6;
            if (result.Item1) await this.SaveDatabaseAsync(node, options);
            return result;
        }

        public async override Task<(bool, string)> ReStart(InstallOptions options)
        {
            var command = $"systemctl restart iToolServer.service";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.ReStartAsync(6);
            return result;
        }

        public async override Task<(bool, string)> Start(InstallOptions options)
        {
            var command = $"systemctl start iToolServer.service";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.StartAsync();
            return result;
        }

        public async override Task<(bool, string)> Stop(InstallOptions options)
        {
            var command = $"systemctl stop iToolServer.service";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.StopAsync(6);
            return result;
        }

        public async override void CheckCreate()
        {
            this.IsNoCreateMessage += ",";

            using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var services = context.ServiceInstances.Select(item => new
                {
                    item.Status,
                    item.Process,
                    item.Type
                }).Where(item => item.Status == 2).ToList();

                //if (!services.Any(item => item.Process.Equals("RabbitMQ")))
                //{
                //    if (this.IsNoCreateMessage.IndexOf("Not RabbitMQ") == -1)
                //    {
                //        this.IsNoCreateMessage += "Not RabbitMQ,";
                //    }
                //}

                //if (!services.Any(item => item.Process.Equals("Redis")))
                //{
                //    if (this.IsNoCreateMessage.IndexOf("Not Redis") == -1)
                //        this.IsNoCreateMessage += "Not Redis,";
                //}

                if (!services.Any(item => item.Process.Equals("MongoDB")))
                {
                    if (this.IsNoCreateMessage.IndexOf("Not MongoDB") == -1)
                        this.IsNoCreateMessage += "Not MongoDB,";
                }

                if (!services.Any(item => item.Process.Equals("Elasticsearch")))
                {
                    if (this.IsNoCreateMessage.IndexOf("Not Elasticsearch") == -1)
                        this.IsNoCreateMessage += "Not Elasticsearch,";
                }

                if (!services.Any(item => item.Process.Equals("SqlServer") && item.Type.IndexOf(Enum.ToObject(typeof(DatabaseType), 0).ToString()) > -1))
                {
                    this.IsNoCreateMessage += $"Not SqlServer > {Enum.ToObject(typeof(DatabaseType), 0)},";
                }

                if (!services.Any(item => item.Process.Equals("SqlServer") && item.Type.IndexOf(Enum.ToObject(typeof(DatabaseType), 1).ToString()) > -1))
                {
                    this.IsNoCreateMessage += $"Not SqlServer > {Enum.ToObject(typeof(DatabaseType), 1)},";
                }

                if (!services.Any(item => item.Process.Equals("SqlServer") && item.Type.IndexOf(Enum.ToObject(typeof(DatabaseType), 2).ToString()) > -1))
                {
                    this.IsNoCreateMessage += $"Not SqlServer > {Enum.ToObject(typeof(DatabaseType), 2)},";
                }

                if (!services.Any(item => item.Process.Equals("SqlServer") && item.Type.IndexOf(Enum.ToObject(typeof(DatabaseType), 3).ToString()) > -1))
                {
                    this.IsNoCreateMessage += $"Not SqlServer > {Enum.ToObject(typeof(DatabaseType), 3)},";
                }

                var iToolConfig = await this.iToolCenter.Get();

                if (iToolConfig == default)
                {
                    this.IsNoCreateMessage += $"Not Set iToolCenter,";
                }

                this.IsNoCreateMessage = this.IsNoCreateMessage.TrimEnd(',').TrimStart(',');

                this.IsCreate = string.IsNullOrWhiteSpace(this.IsNoCreateMessage);

            }
        }

    }

    public class iToolFileCenter : AbstractServer
    {
        public override Task<(bool, string)> Backup(InstallOptions options)
        {
            return Task.FromResult((true, string.Empty));
        }

        public async override Task<(bool, string)> Install(Node node, InstallOptions options)
        {
            var command = $"wget -O installiToolFileCenter.sh https://cloud.itool.store/iToolFileCenter.sh && sudo bash installiToolFileCenter.sh";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.SaveDatabaseAsync(node, options);
            return result;
        }

        public async override Task<(bool, string)> ReStart(InstallOptions options)
        {
            var command = $"systemctl restart iToolFileCenter.service";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.ReStartAsync();
            return result;
        }

        public async override Task<(bool, string)> Start(InstallOptions options)
        {
            var command = $"systemctl start iToolFileCenter.service";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.StartAsync();
            return result;
        }

        public async override Task<(bool, string)> Stop(InstallOptions options)
        {
            var command = $"systemctl stop iToolFileCenter.service";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.StopAsync();
            return result;
        }

        public async override void CheckCreate()
        {
            using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var iToolConfig = await this.iToolCenter.Get();
                if (iToolConfig == default)
                {
                    this.IsNoCreateMessage += $"Not Set iToolCenter";
                }

                this.IsCreate = string.IsNullOrWhiteSpace(this.IsNoCreateMessage);
            }
        }
    }

    public class iToolClient : AbstractServer
    {
        public override Task<(bool, string)> Backup(InstallOptions options)
        {
            return Task.FromResult((true, string.Empty));
        }

        public async override Task<(bool, string)> Install(Node node, InstallOptions options)
        {
            var command = $"wget -O installiToolConnector.sh https://cloud.itool.store/iToolConnector.sh && sudo bash installiToolConnector.sh";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.SaveDatabaseAsync(node, options);
            return result;
        }

        public async override Task<(bool, string)> ReStart(InstallOptions options)
        {
            var command = $"systemctl restart iToolConnector.service";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.ReStartAsync(6);
            return result;
        }

        public async override Task<(bool, string)> Start(InstallOptions options)
        {
            var command = $"systemctl start iToolConnector.service";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.StartAsync();
            return result;
        }

        public async override Task<(bool, string)> Stop(InstallOptions options)
        {
            var command = $"systemctl stop iToolConnector.service";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.StopAsync(6);
            return result;
        }

        public override void CheckCreate()
        {
            using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var iToolService = context.ServiceInstances.Select(item => new
                {
                    item.Status,
                    item.Process
                }).Where(item => item.Status == 2 && item.Process == "iToolService").FirstOrDefault();

                if (iToolService == null || iToolService?.Status != 2)
                {
                    this.IsNoCreateMessage = "Not iToolService";
                }

                this.IsCreate = string.IsNullOrWhiteSpace(this.IsNoCreateMessage);
            }
        }
    }

    public class SqlServer : AbstractServer
    {
        public SqlServer()
        {
            this.FromConfig.User = false;
            this.FromConfig.Password = true;
        }

        public async override Task<(bool, string)> Install(Node node, InstallOptions options)
        {
            var command = $"docker run -e ACCEPT_EULA=Y -e MSSQL_PID=\"Developer\" -e MSSQL_SA_PASSWORD=\"{options.Password}\" -e MSSQL_TCP_PORT={options.Port} -p {options.Port}:{options.Port} --name {options.Title} -d microsoft/mssql-server-linux";
            //var result = await this.iNodeService.ExcuterCommand(options.IP, command, options.databaseType.ToString(), options.Port, options.Password);
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            result.Item1 = result.Item1 ? !string.IsNullOrWhiteSpace(result.Item2) : result.Item1;
            if (result.Item1) await this.SaveDatabaseAsync(node, options);
            return result;
        }

        public async Task<(bool, string)> CreateiToolDatabase(InstallOptions options)
        {
            this.ServiceInstance.Status = 1;
            var result = await this.iNodeService.ExcuterCommand(options.IP, string.Empty, options.databaseType, options.Port, options.Password);
            if (result.Item1)
            {
                await using (iToolGatewayContext context = new iToolGatewayContext())
                {
                    var instance = await context.ServiceInstances.FindAsync(this.ServiceInstance.ServiceInstanceId);

                    if (string.IsNullOrWhiteSpace(instance.Type))
                    {
                        instance.Type = options.databaseType;
                    }
                    else
                    {
                        instance.Type += ("," + options.databaseType);
                    }

                    this.ServiceInstance.Status = 2;
                    await context.SaveChangesAsync();
                }
            }
            return result;
        }

        public async Task<(bool, string)> DropiToolDatabase(InstallOptions options, string dbType)
        {
            var masterConnaction = $"Data Source={options.IP},{options.Port};database=master;uid=sa;pwd={options.Password};MultipleActiveResultSets=true;";
            string dbName = string.Empty;
            switch (dbType)
            {
                case "iToolBase":
                    dbName = "iTool.CloudBase";
                    break;
                case "Logger":
                    dbName = "iTool.Logger";
                    break;
                case "Function":
                    dbName = "iTool.Function";
                    break;
                case "Message":
                    dbName = "iTool.Message";
                    break;
            }

            try
            {
                await this.CheckDatabaseExistsAndDrop(masterConnaction, dbName);

                await using (iToolGatewayContext context = new iToolGatewayContext())
                {
                    var instance = await context.ServiceInstances.FindAsync(this.ServiceInstance.ServiceInstanceId);

                    if (!string.IsNullOrWhiteSpace(instance.Type))
                    {
                        instance.Type = instance.Type.Replace(dbType, "").Replace(",,", ",").TrimStart(',').TrimEnd(',');
                        this.ServiceInstance.Type = instance.Type;
                        await context.SaveChangesAsync();
                    }
                }

                return (true, $"{dbName} 已经被删除。");
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }

        private async Task CheckDatabaseExistsAndDrop(string masterConnaction, string dbName)
        {
            await using (SqlConnection conn = new SqlConnection(masterConnaction))
            {
                await using (SqlCommand command = new SqlCommand($"SELECT database_id from sys.databases WHERE Name  = '{dbName}'", conn))
                {
                    await conn.OpenAsync();

                    object resultObj = await command.ExecuteScalarAsync();

                    int databaseID = 0;

                    if (resultObj != null)
                    {
                        int.TryParse(resultObj.ToString(), out databaseID);
                    }

                    if (databaseID > 0)
                    {
                        await using (SqlCommand dropCommand = new SqlCommand($"DROP DATABASE [{dbName}]", conn))
                        {
                            await dropCommand.ExecuteNonQueryAsync();
                        }
                    }
                }
            }
        }

    }

    //public class RabbitMQ : AbstractServer
    //{
    //    public RabbitMQ()
    //    {
    //        this.FromConfig.User = true;
    //        this.FromConfig.Password = true;
    //    }

    //    public async override Task<(bool, string)> Install(Node node, InstallOptions options)
    //    {
    //        var command = $"docker run -d --hostname itool-{options.Title} --name {options.Title} -e RABBITMQ_DEFAULT_USER={options.User} -e RABBITMQ_DEFAULT_PASS={options.Password} -p {options.Port+1000}:15672 -p {options.Port}:5672 rabbitmq:management";
    //        var result = await this.iNodeService.ExcuterCommand(options.IP, command);
    //        result.Item1 = result.Item1 ? !string.IsNullOrWhiteSpace(result.Item2) : result.Item1;
    //        if (result.Item1) await this.SaveDatabaseAsync(node, options);
    //        return result;
    //    }
    //}

    public class Elasticsearch : AbstractServer
    {
        public Elasticsearch()
        {
            this.FromConfig.User = true;
            this.FromConfig.Password = true;
        }

        public async override Task<(bool, string)> Install(Node node, InstallOptions options)
        {
            var command = $"docker run --name {options.Title} -d -e ES_JAVA_OPTS=\"-Xms512m -Xmx512m\" -e \"discovery.type=single-node\" -p {options.Port}:9200 -p {options.Port + 1000}:9300 elasticsearch:7.12.1";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            result.Item1 = result.Item1 ? !string.IsNullOrWhiteSpace(result.Item2) : result.Item1;
            if (result.Item1) await this.SaveDatabaseAsync(node, options);
            return result;
        }
    }

    public class MongoDB : AbstractServer
    {
        public MongoDB()
        {
            this.FromConfig.User = true;
            this.FromConfig.Password = true;
        }

        public async override Task<(bool, string)> Install(Node node, InstallOptions options)
        {
            var command = $"docker run --name {options.Title} -p {options.Port}:27017 -v /data/mongo:/data/db -e MONGO_INITDB_ROOT_USERNAME={options.User} -e MONGO_INITDB_ROOT_PASSWORD={options.Password} -d mongo";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            result.Item1 = result.Item1 ? !string.IsNullOrWhiteSpace(result.Item2) : result.Item1;
            if (result.Item1) await this.SaveDatabaseAsync(node, options);
            return result;
        }

    }

    //public class Redis : AbstractServer
    //{
    //    public Redis()
    //    {
    //        this.FromConfig.Password = true;
    //    }

    //    public async override Task<(bool, string)> Install(Node node, InstallOptions options)
    //    {
    //        var command = $"docker run --name {options.Title} -p {options.Port}:6379 -d redis:latest redis-server --requirepass {options.Password}";
    //        var result = await this.iNodeService.ExcuterCommand(options.IP, command);
    //        result.Item1 = result.Item1 ? !string.IsNullOrWhiteSpace(result.Item2) : result.Item1;
    //        if(result.Item1) await this.SaveDatabaseAsync(node,options);
    //        return result;
    //    }
    //}

    public abstract class AbstractServer
    {
        public bool IsStartCreate { get; set; }
        public bool Enable { get; set; }
        public bool OpenDrawerEnable { get; set; }
        public bool Enableing { get; set; }
        public bool IsCreate { get; set; } = true;
        public string IsNoCreateMessage { get; set; }
        public FromConfig FromConfig { get; set; } = new FromConfig();
        public ServiceInstance ServiceInstance { get; set; }
        public INodeService iNodeService { get; set; }
        public IToolCenter iToolCenter { get; set; }

        public AbstractServer()
        {
            this.iNodeService = ProviderOfServices.GetService<INodeService>();
            this.iToolCenter = ProviderOfServices.GetService<IToolCenter>();
            this.CheckCreate();
        }

        public virtual void CheckCreate()
        {
            IsCreate = true;
            IsNoCreateMessage = string.Empty;
        }

        public abstract Task<(bool, string)> Install(Node node, InstallOptions options);

        public async virtual Task<(bool, string)> Backup(InstallOptions options)
        {
            var command = $"docker commit -p {options.Title} {options.Title}-backup-{DateTime.Now.ToString("d")}";
            return await this.iNodeService.ExcuterCommand(options.IP, command);
        }

        public async virtual Task<(bool, string)> ReStart(InstallOptions options)
        {
            this.ServiceInstance.Status = 3;
            var command = $"docker restart {options.Title}";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.ReStartAsync();
            else
            {
                this.ServiceInstance.Status = 2;
            }
            return result;
        }

        public async virtual Task<(bool, string)> Start(InstallOptions options)
        {
            var command = $"docker start {options.Title}";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.StartAsync();
            return result;
        }

        public async virtual Task<(bool, string)> Stop(InstallOptions options)
        {
            var command = $"docker stop {options.Title}";
            var result = await this.iNodeService.ExcuterCommand(options.IP, command);
            if (result.Item1) await this.StopAsync();
            return result;
        }

        public async virtual Task<(bool, string)> Remove(InstallOptions options)
        {
            await this.iNodeService.ExcuterCommand(options.IP, $"docker stop {options.Title}");
            var result = await this.iNodeService.ExcuterCommand(options.IP, $"docker rm {options.Title}");
            if (result.Item1) await this.RemoveAsync();
            return result;
        }

        protected async Task<bool> SaveDatabaseAsync(Node item, InstallOptions options)
        {

            try
            {
                var process = this.GetType().Name;

                await using (iToolGatewayContext context = new iToolGatewayContext())
                {
                    this.ServiceInstance ??= new ServiceInstance();
                    this.ServiceInstance.NodeIP = options.IP;
                    this.ServiceInstance.Title = options.Title;
                    this.ServiceInstance.NodeId = options.NodeId;
                    this.ServiceInstance.User = options.User;
                    this.ServiceInstance.Password = options.Password;
                    this.ServiceInstance.Port = options.Port;
                    this.ServiceInstance.Type = options.databaseType ?? string.Empty;
                    this.ServiceInstance.Status = options.Status;
                    this.ServiceInstance.IsRunInDocker = !new string[] { "iToolClient", "iToolService", "iToolFileCenter" }.Contains(process);
                    this.ServiceInstance.Process = process;
                    this.ServiceInstance.LastCheckTime = DateTime.UtcNow;
                    await context.ServiceInstances.AddAsync(this.ServiceInstance);
                    await context.SaveChangesAsync();
                    item.ServiceInstances.Add(this.ServiceInstance);
                    return this.ServiceInstance.ServiceInstanceId > 0;
                }
            }
            catch (Exception ex)
            {
                LoggerUtils.Write($"SaveDatabaseAsync", ex);
                return false;
            }
        }

        protected async Task<bool> ReStartAsync(int status = 2)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var instance = await context.ServiceInstances.FindAsync(this.ServiceInstance.ServiceInstanceId);
                instance.Status = status;
                this.ServiceInstance.Status = status;
                await context.SaveChangesAsync();
                return true;
            }
        }

        protected async Task<bool> StopAsync(int status = 4)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var instance = await context.ServiceInstances.FindAsync(this.ServiceInstance.ServiceInstanceId);
                instance.Status = status;
                instance.Cpu = "0.00%";
                instance.Memory = "0.00MiB";

                this.ServiceInstance.Status = status;
                this.ServiceInstance.Cpu = "0.00%";
                this.ServiceInstance.Memory = "0.00MiB";
                await context.SaveChangesAsync();
                return true;
            }
        }

        protected async Task<bool> RemoveAsync()
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var instance = await context.ServiceInstances.FindAsync(this.ServiceInstance.ServiceInstanceId);
                context.ServiceInstances.Remove(instance);
                await context.SaveChangesAsync();
                return true;
            }
        }

        protected async Task<bool> StartAsync()
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var instance = await context.ServiceInstances.FindAsync(this.ServiceInstance.ServiceInstanceId);
                instance.Status = 2;
                this.ServiceInstance.Status = 2;
                await context.SaveChangesAsync();
                return true;
            }
        }

    }

    public class FromConfig
    {
        public bool User { get; set; }
        public bool Password { get; set; }
    }

    public class InstallOptions
    {
        public int NodeId { get; set; }
        public string IP { get; set; }
        public string Title { get; set; }
        public int Port { get; set; }
        public string User { get; set; }
        public string Password { get; set; }
        public string databaseType { get; set; }
        public int Status { get; set; } = 2;
    }
    public enum DatabaseType
    {
        Logger,
        Function,
        Message,
        iToolBase,
        NULL
    }
}
